package lox

class Environment(internal val enclosing: Environment? = null, private val values: HashMap<String, Any?> = HashMap()) {
    fun define(name: String, value: Any?) = values.put(name, value)

    operator fun get(name: Token): Any? {
        if (values.containsKey(name.lexeme)) {
            return values[name.lexeme]
        }

        return when (enclosing) {
            null -> throw RuntimeError(name, "Undefined variable '${name.lexeme}'.")
            else -> enclosing[name]
        }
    }

    fun assign(name: Token, value: Any?) {
        if (values.containsKey(name.lexeme)) {
            values[name.lexeme] = value
            return
        }

        return when (enclosing) {
            null -> throw RuntimeError(name, "Undefined variable '${name.lexeme}'.")
            else -> enclosing.assign(name, value)
        }
    }

    fun getAt(distance: Int, name: String): Any? = ancestor(distance).values[name]

    private fun ancestor(distance: Int): Environment {
        var env = this
        repeat(distance) {
            env = env.enclosing!!
        }

        return env
    }

    fun assignAt(distance: Int, name: Token, value: Any?) {
        ancestor(distance).values[name.lexeme] = value
    }


}